#include "optflow_feat_def.hpp"
#include "optflow_feat_impl.hpp"

#include "edt_feat_def.hpp"
#include "edt_feat_impl.hpp"

#include "mask_optflow_feat_def.hpp"
#include "mask_optflow_feat_impl.hpp"


inline 
fisher_vector::fisher_vector(const string  in_Spath,
			 const string  in_Mpath,
			 const string  in_actionNames, 
			 const uword in_col, 
			 const uword in_row)
:single_path(in_Spath), multi_path(in_Mpath), actionNames(in_actionNames), col(in_col), row(in_row)
{
  actions.load( actionNames );  //actions.print("All actions");
  dim = 14 ;
}

//*********************************************************************************
//*******************optical flow and gradient features****************************
//*********************************************************************************


inline
void
fisher_vector::optflow_features_allpeople(field <string>  people)
{
  opt_feat optflow(actionNames, col, row, dim);
  optflow.features_allpeople( people, single_path ); 
}

inline
void
fisher_vector::optflow_features_testing( field <string>  people)
{
  opt_feat optflow(actionNames, col, row, dim);
  optflow.feature_multi_action( people, multi_path ); 
}

inline
void
fisher_vector::optflow_create_universal_gmm(field <string>  peo_train, int N_cent, int run)
{ 
  
  cout << "Calculating Universal GMM " << endl;
  cout << "# clusters: " << N_cent << endl;
  
  mat uni_features;
  
   for (uword pe=0; pe<peo_train.n_rows; ++pe)    {
     
     mat mat_features_tmp;
     mat mat_features;
     
     for (uword act = 0 ; act < actions.n_rows;  ++act) {
       
       
       mat mat_features_video_i;
       std::stringstream ssName_feat_video;
       ssName_feat_video << "./features_training/feat_vec_" << peo_train(pe) << "_" << actions(act);
       //cout << ssName_feat_video.str() << endl;
       mat_features_video_i.load( ssName_feat_video.str() );
       
       if ( mat_features_video_i.n_cols>0 )
       {
	 mat_features_tmp	 = join_rows( mat_features_tmp, mat_features_video_i );
	 
      }
      else 
      {
	cout << "# vectors = 0 in " << ssName_feat_video.str() << endl;
	
      }

       
    }
    
    cout << "mat_features_tmp.n_cols "<< mat_features_tmp.n_cols << endl;
    const uword N_max = 100000;  
    
    if (mat_features_tmp.n_cols > N_max)
    {
      ivec tmp1 = randi( N_max, distr_param(0,mat_features_tmp.n_cols-1) );
      ivec tmp2 = unique(tmp1);
      uvec my_indices = conv_to<uvec>::from(tmp2);
      mat_features = mat_features_tmp.cols(my_indices);  // extract a subset of the columns
      
    }
    else
    {
      mat_features = mat_features_tmp;
      
    }
    
      cout << "mat_features.n_cols "<< mat_features.n_cols << endl;
    
      if ( mat_features.n_cols>0 )
      {
	uni_features	 = join_rows( uni_features, mat_features );
	
      }
      else 
      {
	cout << "# vectors = 0 in uni_features" << endl;
	
      }
    
    //uni_features =  join_rows( uni_features, mat_features );
    mat_features_tmp.reset();
    mat_features.reset();
    
    
  }
  
  
  cout << "r&c "<<  uni_features.n_rows << " & " << uni_features.n_cols << endl;
  bool is_finite = uni_features.is_finite();

    if (!is_finite )
    {
      
      cout << "is_finite?? " << is_finite << endl;
      cout << uni_features.n_rows << " " << uni_features.n_cols << endl;
      getchar();
    
    }
  
  
  cout << "universal GMM" << endl;
  gmm_diag gmm_model;
  gmm_diag bg_model;
  
  bool status_em = false;
  int rep_em=0;
   
  int km_iter = 20;
  int em_iter  = 20;
  double var_floor = 1e-10;
  bool print_mode = true;
  
  
  
  
  while (!status_em)
  {
    bool status_kmeans = false;
    //int rep_km = 0;
    
    while (!status_kmeans)
    {
      arma_rng::set_seed_random();
      
      status_kmeans = gmm_model.learn(uni_features, N_cent, eucl_dist, random_subset, km_iter, 0, var_floor, print_mode);   //Only Kmeans
      bg_model = gmm_model;
      //rep_km++;
    }
    
    
    status_em = gmm_model.learn(uni_features, N_cent, eucl_dist, keep_existing, 0, em_iter, var_floor, print_mode);   
    rep_em++;
    
    if (rep_em==9)
    {
      status_em = true;
      gmm_model = bg_model;
      
    }
    
  }
  
  
  cout <<"EM was repeated " << rep_em << endl;
  
  std::stringstream tmp_ss5;
  tmp_ss5 << "./run"<< run <<"/universal_GMM/UniversalGMM_Ng" << N_cent << "_dim" <<dim;
  cout << "Saving GMM in " << tmp_ss5.str() << endl;
  gmm_model.save( tmp_ss5.str() );
  cout << endl;
  
  
  means = gmm_model.means;
  covs  = gmm_model.dcovs;
  weights = gmm_model.hefts.t();	
  
  //Saving statistics
  std::stringstream ss_weigths;
  ss_weigths << "./run"<< run <<"/universal_GMM/weights_Ng" << N_cent << "_dim" << dim; 
  
  std::stringstream ss_means;
  ss_means << "./run"<< run <<"/universal_GMM/means_Ng" << N_cent << "_dim" << dim; 
  
  std::stringstream ss_covs;
  ss_covs << "./run"<< run <<"/universal_GMM/covs_Ng" << N_cent << "_dim" << dim; 
  
  weights.save( ss_weigths.str(), raw_ascii );
  means.save( ss_means.str(), raw_ascii );
  covs.save(ss_covs.str(), raw_ascii);

}



//*********************************************************************************
//*******************MASK optical flow and gradient features***********************
//*********************************************************************************

inline
void
fisher_vector::mask_optflow_features_allpeople(field <string>  people, string single_path_mask )
{
  mask_opt_feat mask_optflow(actionNames, col, row, dim);
  mask_optflow.features_allpeople( people, single_path, single_path_mask  ); 
}

inline
void
fisher_vector::mask_optflow_features_testing( field <string>  people, string multi_path_mask)
{
  mask_opt_feat mask_optflow(actionNames, col, row, dim);
  mask_optflow.feature_multi_action( people, multi_path, multi_path_mask ); 
}


inline
void
fisher_vector::mask_optflow_create_universal_gmm(field <string>  peo_train, int N_cent, int run)
{ 
  
  cout << "Calculating Universal GMM " << endl;
  cout << "# clusters: " << N_cent << endl;
  
  mat uni_features;
  
   for (uword pe=0; pe<peo_train.n_rows; ++pe)    {
     
     mat mat_features_tmp;
     mat mat_features;
     
     for (uword act = 0 ; act < actions.n_rows;  ++act) {
       
       
       mat mat_features_video_i;
       std::stringstream ssName_feat_video;
       ssName_feat_video << "./mask_opt_feat_training/mask_feat_vec_" << peo_train(pe) << "_" << actions(act);
       //cout << ssName_feat_video.str() << endl;
       mat_features_video_i.load( ssName_feat_video.str() );
       
       if ( mat_features_video_i.n_cols>0 )
       {
	 mat_features_tmp	 = join_rows( mat_features_tmp, mat_features_video_i );
	 
      }
      else 
      {
	cout << "# vectors = 0 in " << ssName_feat_video.str() << endl;
	
      }

       
    }
    
    cout << "mat_features_tmp.n_cols "<< mat_features_tmp.n_cols << endl;
    const uword N_max = 100000;  
    
    if (mat_features_tmp.n_cols > N_max)
    {
      ivec tmp1 = randi( N_max, distr_param(0,mat_features_tmp.n_cols-1) );
      ivec tmp2 = unique(tmp1);
      uvec my_indices = conv_to<uvec>::from(tmp2);
      mat_features = mat_features_tmp.cols(my_indices);  // extract a subset of the columns
      
    }
    else
    {
      mat_features = mat_features_tmp;
      
    }
    
      cout << "mat_features.n_cols "<< mat_features.n_cols << endl;
    
      if ( mat_features.n_cols>0 )
      {
	uni_features	 = join_rows( uni_features, mat_features );
	
      }
      else 
      {
	cout << "# vectors = 0 in uni_features" << endl;
	
      }
    
    //uni_features =  join_rows( uni_features, mat_features );
    mat_features_tmp.reset();
    mat_features.reset();
    
    
  }
  
  
  cout << "r&c "<<  uni_features.n_rows << " & " << uni_features.n_cols << endl;
  bool is_finite = uni_features.is_finite();

    if (!is_finite )
    {
      
      cout << "is_finite?? " << is_finite << endl;
      cout << uni_features.n_rows << " " << uni_features.n_cols << endl;
      getchar();
    
    }
  
  
  cout << "universal GMM" << endl;
  gmm_diag gmm_model;
  gmm_diag bg_model;
  
  bool status_em = false;
  int rep_em=0;
   
  int km_iter = 20;
  int em_iter  = 20;
  double var_floor = 1e-10;
  bool print_mode = true;
  
  
  
  
  while (!status_em)
  {
    bool status_kmeans = false;
    //int rep_km = 0;
    
    while (!status_kmeans)
    {
      arma_rng::set_seed_random();
      
      status_kmeans = gmm_model.learn(uni_features, N_cent, eucl_dist, random_subset, km_iter, 0, var_floor, print_mode);   //Only Kmeans
      bg_model = gmm_model;
      //rep_km++;
    }
    
    
    status_em = gmm_model.learn(uni_features, N_cent, eucl_dist, keep_existing, 0, em_iter, var_floor, print_mode);   
    rep_em++;
    
    if (rep_em==9)
    {
      status_em = true;
      gmm_model = bg_model;
      
    }
    
  }
  
  
  cout <<"EM was repeated " << rep_em << endl;
  
  std::stringstream tmp_ss5;
  tmp_ss5 << "./run"<< run <<"/universal_GMM/opt_flow_mask_UniversalGMM_Ng" << N_cent << "_dim" <<dim;
  cout << "Saving GMM in " << tmp_ss5.str() << endl;
  gmm_model.save( tmp_ss5.str() );
  cout << endl;
  
  
  means = gmm_model.means;
  covs  = gmm_model.dcovs;
  weights = gmm_model.hefts.t();	
  
  //Saving statistics
  std::stringstream ss_weigths;
  ss_weigths << "./run"<< run <<"/universal_GMM/opt_flow_mask_weights_Ng" << N_cent << "_dim" << dim; 
  
  std::stringstream ss_means;
  ss_means << "./run"<< run <<"/universal_GMM/opt_flow_mask_means_Ng" << N_cent << "_dim" << dim; 
  
  std::stringstream ss_covs;
  ss_covs << "./run"<< run <<"/universal_GMM/opt_flow_mask_covs_Ng" << N_cent << "_dim" << dim; 
  
  weights.save( ss_weigths.str(), raw_ascii );
  means.save( ss_means.str(), raw_ascii );
  covs.save(ss_covs.str(), raw_ascii);

}




//*********************************************************************************
//***********************Euclidean Distance Transform******************************
//*********************************************************************************

inline
void
fisher_vector::edt_features_allpeople(field <string>  people, string single_path_mask)
{
  edt_feat edtfeat(actionNames, col, row, dim);
  edtfeat.features_allpeople( people, single_path, single_path_mask ); 
}



inline
void
fisher_vector::edt_features_testing( field <string>  people, string multi_path_mask )
{
  edt_feat edtfeat(actionNames, col, row, dim);
  edtfeat.feature_multi_action( people, multi_path, multi_path_mask ); 
}



inline
void
fisher_vector::edt_create_universal_gmm(field <string>  peo_train, int N_cent, int run, bool is_pca)
{ 
  
  cout << "Calculating Universal GMM " << endl;
  cout << "# clusters: " << N_cent << endl;
  
  mat uni_features;
  
   for (uword pe=0; pe<peo_train.n_rows; ++pe)    {
     
     mat mat_features_tmp;
     mat mat_features;
     
     for (uword act = 0 ; act < actions.n_rows;  ++act) {
       
       
       mat mat_features_video_i;
       std::stringstream ssName_feat_video;
       ssName_feat_video << "./edt_features_training/edt_feat_vec_" << peo_train(pe) << "_" << actions(act);
       //cout << ssName_feat_video.str() << endl;
       mat_features_video_i.load( ssName_feat_video.str() );
       
       if ( mat_features_video_i.n_cols>0 )
       {
	 mat_features_tmp	 = join_rows( mat_features_tmp, mat_features_video_i );
	 
      }
      else 
      {
	cout << "# vectors = 0 in " << ssName_feat_video.str() << endl;
      }

    }
    
    cout << "mat_features_tmp.n_cols "<< mat_features_tmp.n_cols << endl;
    const uword N_max = 100000;  
    
    if (mat_features_tmp.n_cols > N_max)
    {
      ivec tmp1 = randi( N_max, distr_param(0,mat_features_tmp.n_cols-1) );
      ivec tmp2 = unique(tmp1);
      uvec my_indices = conv_to<uvec>::from(tmp2);
      mat_features = mat_features_tmp.cols(my_indices);  // extract a subset of the columns
      
    }
    else
    {
      mat_features = mat_features_tmp;
      
    }
    
      cout << "mat_features.n_cols "<< mat_features.n_cols << endl;
    
      if ( mat_features.n_cols>0 )
      {
	uni_features	 = join_rows( uni_features, mat_features );
	
      }
      else 
      {
	cout << "# vectors = 0 in uni_features" << endl;
	
      }
    
    //uni_features =  join_rows( uni_features, mat_features );
    mat_features_tmp.reset();
    mat_features.reset();
    
    
  }
  
  
  cout << "r&c "<<  uni_features.n_rows << " & " << uni_features.n_cols << endl;
  
  //PCA:
  if (is_pca)
  {
  mat U;
  vec s;
  mat V;
  mat Sigma = cov( uni_features.t() );
  svd(U,s,V,Sigma);
  int dim_data = uni_features.n_rows;
  int n_vec_data = uni_features.n_cols;
  int NP = dim_data; //NO Reducing dimension. Only trasnforming the space
  mat trans_matrix_pca = U.cols( 0, NP-1 ); //Transformation Matrix
  
  mat tpr = uni_features.t()*trans_matrix_pca;

  uni_features = tpr.t();
  
  //cout << "U r&c "<<  U.n_rows << " & " << U.n_cols << endl;

  //cout << "Sigma r&c "<<  Sigma.n_rows << " & " << Sigma.n_cols << endl;
  
  //cout << "trans_matrix_pca r&c "<<  trans_matrix_pca.n_rows << " & " << trans_matrix_pca.n_cols << endl;
  //cout << "tpr r&c "<<  tpr.n_rows << " & " << tpr.n_cols << endl;
  //cout << "new uni_features r&c "<<  uni_features.n_rows << " & " << uni_features.n_cols << endl;
  
  std::stringstream tmp_pca;
  tmp_pca << "./run"<< run <<"/edt_universal_GMM/trans_matrix_pca.dat";
  cout << "Saving trans_matrix_pca in " << tmp_pca.str() << endl;
  trans_matrix_pca.save( tmp_pca.str(), raw_ascii );
  //save 
  }
  
  
  
  bool is_finite = uni_features.is_finite();
 
    if (!is_finite )
    {
      cout << "is_finite?? " << is_finite << endl;
      cout << uni_features.n_rows << " " << uni_features.n_cols << endl;
      getchar();
    
    }
  
  
  cout << "universal GMM" << endl;
  gmm_diag gmm_model;
  gmm_diag bg_model;
  
  bool status_em = false;
  int rep_em=0;
   
  int km_iter = 20;
  int em_iter  = 20;
  double var_floor = 1e-10;
  bool print_mode = true;
  
  
  
  
  while (!status_em)
  {
    bool status_kmeans = false;
    //int rep_km = 0;
    
    while (!status_kmeans)
    {
      arma_rng::set_seed_random();
      
      status_kmeans = gmm_model.learn(uni_features, N_cent, eucl_dist, random_subset, km_iter, 0, var_floor, print_mode);   //Only Kmeans
      bg_model = gmm_model;
      //rep_km++;
    }
    
    
    status_em = gmm_model.learn(uni_features, N_cent, eucl_dist, keep_existing, 0, em_iter, var_floor, print_mode);   
    rep_em++;
    
    if (rep_em==9)
    {
      status_em = true;
      gmm_model = bg_model;
      
    }
    
  }
  
  
  cout <<"EM was repeated " << rep_em << endl;
  
  
  if (is_pca)
  {
  std::stringstream tmp_ss5;
  tmp_ss5 << "./run"<< run <<"/edt_universal_GMM/pca_UniversalGMM_Ng" << N_cent;
  cout << "Saving GMM in " << tmp_ss5.str() << endl;
  gmm_model.save( tmp_ss5.str() );
  cout << endl;
  
  
  means = gmm_model.means;
  covs  = gmm_model.dcovs;
  weights = gmm_model.hefts.t();	
  
  //Saving statistics
  std::stringstream ss_weigths;
  ss_weigths << "./run"<< run <<"/edt_universal_GMM/pca_edt_weights_Ng" << N_cent; 
  
  std::stringstream ss_means;
  ss_means << "./run"<< run <<"/edt_universal_GMM/pca_edt_means_Ng" << N_cent; 
  
  std::stringstream ss_covs;
  ss_covs << "./run"<< run <<"/edt_universal_GMM/pca_edt_covs_Ng" << N_cent; 
  
  weights.save( ss_weigths.str(), raw_ascii );
  means.save( ss_means.str(), raw_ascii );
  covs.save(ss_covs.str(), raw_ascii);
  }
  
  else
  {
    
    std::stringstream tmp_ss5;
    tmp_ss5 << "./run"<< run <<"/edt_universal_GMM/UniversalGMM_Ng" << N_cent;
    cout << "Saving GMM in " << tmp_ss5.str() << endl;
    gmm_model.save( tmp_ss5.str() );
    cout << endl;
    means = gmm_model.means;
    covs  = gmm_model.dcovs;
    weights = gmm_model.hefts.t();	
  
  //Saving statistics
  std::stringstream ss_weigths;
  ss_weigths << "./run"<< run <<"/edt_universal_GMM/edt_weights_Ng" << N_cent; 
  
  std::stringstream ss_means;
  ss_means << "./run"<< run <<"/edt_universal_GMM/edt_means_Ng" << N_cent; 
  
  std::stringstream ss_covs;
  ss_covs << "./run"<< run <<"/edt_universal_GMM/edt_covs_Ng" << N_cent; 
  
  weights.save( ss_weigths.str(), raw_ascii );
  means.save( ss_means.str(), raw_ascii );
  covs.save(ss_covs.str(), raw_ascii);}

}